Understanding Node Affinity in Kubernetes

Introduction

Node affinity is an advanced feature in Kubernetes that allows you to control where pods are scheduled by setting complex rules for pod placement. While Node Selectors offer a simpler approach, Node Affinity provides more flexibility with expressions like In, NotIn, and Exists, giving you greater control over pod placement based on node labels.

What is Node Affinity?

Node affinity allows you to control which nodes a pod can be scheduled on by defining affinity rules based on node labels. These rules can be mandatory or preferred, depending on the pod's scheduling requirements. This ensures that workloads are deployed on the correct nodes with specific characteristics such as region, instance type, or resource capacities.

Types of Node Affinity

Example: Using Node Affinity

Step 1: Labeling a Node

Before using node affinity, you need to label the nodes. Here’s how to label a node:

kubectl label nodes node1 size=large

This command adds the label size=large to node1.

Step 2: Creating a Pod with Node Affinity

Let’s create a pod that uses node affinity to ensure it runs only on nodes labeled size=large:

apiVersion: v1
kind: Pod
metadata:
  name: data-processor
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: size
            operator: In
            values:
            - large
  containers:
    - name: data-processor-container
      image: data-processor:latest

In this example, the pod will only be scheduled on nodes with the label size=large. The In operator allows us to specify a list of values, but in this case, we are only targeting nodes labeled large.

Step 3: Checking Pod Status

To check if the pod is scheduled correctly, run:

kubectl get pods -o wide

Node Affinity Expressions

Node affinity supports a variety of operators that can be used to define scheduling rules:

Example: Multiple Node Labels

apiVersion: v1
kind: Pod
metadata:
  name: data-processor
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: size
            operator: In
            values:
            - large
            - medium
          - key: region
            operator: In
            values:
            - us-west-1
  containers:
    - name: data-processor-container
      image: data-processor:latest

In this example, the pod will only be scheduled on nodes that have the label size set to either large or medium and the label region set to us-west-1.

Pros and Cons of Node Affinity

Pros

Cons

Tip: Use PreferredDuringSchedulingIgnoredDuringExecution when pod placement is not crucial, but you still want to guide the scheduler toward specific nodes. For critical workloads, use RequiredDuringSchedulingIgnoredDuringExecution to ensure that pods are scheduled on the right nodes.

Real-World Example: Scheduling on Large Compute Nodes

Imagine you have a large data processing application that needs to run on nodes with significant CPU and memory resources. You can use node affinity to ensure that the pods are scheduled on large compute nodes, ensuring they get the resources they need.

Step 1: Labeling the Node

kubectl label nodes compute-node-1 size=large

Step 2: Defining Pod Affinity

apiVersion: v1
kind: Pod
metadata:
  name: big-data-app
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: size
            operator: In
            values:
            - large
  containers:
    - name: big-data-container
      image: big-data-app:latest

Conclusion

Node affinity gives you more control over where your pods are placed in a Kubernetes cluster. It allows you to define rules for pod scheduling based on node labels and is especially useful in heterogeneous environments with diverse node resources. However, it requires careful configuration, and its flexibility comes with some complexity. As Kubernetes continues to evolve, additional node affinity features may be introduced, such as handling changes during pod execution.